package gee

import (
	"encoding/json"
	"fmt"
	"net/http"
)

// change:
// 1 Context add Params about middleware
// 2 add new method Next()

// gee.H 构建数据的别名
type H map[string]interface{}

// Context 上下文结构
type Context struct {
	// origin objects
	Write http.ResponseWriter
	Req   *http.Request

	// request info
	Path   string
	Method string
	Params map[string]string // 路由参数

	// response info
	StatusCode int

	// middleware
	handlers []HandleFunc
	index    int // 记录当前执行到第几个中间件

	engine *Engine // Engine的指针
}

func newContext(w http.ResponseWriter, r *http.Request) *Context {
	return &Context{
		Write:  w,
		Req:    r,
		Path:   r.URL.Path,
		Method: r.Method,
		index:  -1,
	}
}

// 用index记录当前执行的中间件序号，默认-1，所以index++为0开始执行
// 中间件中调用本方法Next, 控制权交给下一个中间件，直到调用到最后一个中间件，再从后往前，调用Next方法后定义的部分
func (c *Context) Next() {
	c.index++
	s := len(c.handlers)
	for ; c.index < s; c.index++ {
		c.handlers[c.index](c)
	}
}

func (c *Context) Fail(code int, err string) {
	c.index = len(c.handlers)
	c.JSON(code, H{"message": err})
}

// 获取参数，eg：c.Param("lang")
func (c *Context) Param(key string) string {
	val, _ := c.Params[key]
	return val
}

// 访问PostForm参数和Query的方法

func (c *Context) PostForm(key string) string {
	return c.Req.FormValue(key)
}

func (c *Context) Query(key string) string {
	return c.Req.URL.Query().Get(key)
}

func (c *Context) Status(code int) {
	c.StatusCode = code
	c.Write.WriteHeader(code)
}

func (c *Context) SetHeader(key string, val string) {
	c.Write.Header().Set(key, val)
}

// 快速构造String/Data/Json/Html响应的方法

func (c *Context) String(code int, format string, values ...interface{}) {
	c.SetHeader("Content-Type", "text/plain")
	c.Status(code)
	c.Write.Write([]byte(fmt.Sprintf(format, values...)))
}

func (c *Context) JSON(code int, obj interface{}) {
	c.SetHeader("Content-Type", "application/json")
	c.Status(code)
	encoder := json.NewEncoder(c.Write)
	if err := encoder.Encode(obj); err != nil {
		http.Error(c.Write, err.Error(), 500)
	}
}

func (c *Context) Data(code int, data []byte) {
	c.Status(code)
	c.Write.Write(data)
}

func (c *Context) HTML(code int, name string, data interface{}) {
	c.SetHeader("Content-Type", "text/html")
	c.Status(code)
	if err := c.engine.htmlTemplates.ExecuteTemplate(c.Write, name, data); err != nil {
		c.Fail(500, err.Error())
	}
}
